package org.etom.impl.connector.util;

import java.net.MalformedURLException;
import java.util.Map;

import org.etom.connector.datatypes.IMoodleConnection;
import org.etom.connector.datatypes.IMoodleCourse;
import org.etom.connector.datatypes.IMoodleModule;
import org.etom.connector.datatypes.IMoodleUser;
import org.etom.connector.service.ConnectorException;
import org.etom.impl.connector.datatypes.MoodleCourseImpl;
import org.etom.impl.connector.datatypes.MoodleModuleImpl;
import org.etom.impl.connector.datatypes.MoodleUserImpl;

import redstone.xmlrpc.XmlRpcArray;
import redstone.xmlrpc.XmlRpcClient;
import redstone.xmlrpc.XmlRpcException;
import redstone.xmlrpc.XmlRpcFault;
import redstone.xmlrpc.XmlRpcStruct;

/**
 * A wrapper class around the xml rpc requests.
 */
public class XmlRpcClientWrapper {
  
  private static final String MODULE_NAME_RESOURCE   = "resource";
  private static final String MODULE_NAME_ASSIGNMENT = "assignment";
  
  private static final String RPC_LIB_URL = "mod/etom/rpclib.php";
  
  private static final String METHOD_URL_GET_USER_COURSES = RPC_LIB_URL + "/get_user_cources";
  private static final String METHOD_URL_GET_USER = RPC_LIB_URL + "/get_user";
  private static final String METHOD_URL_GET_COURSE_MODULES = RPC_LIB_URL + "/get_course_modules";
  private static final String METHOD_URL_GET_RESOURCE_URL = RPC_LIB_URL + "/get_resource_url";
  
  private XmlRpcClient xmlRpcClient;
  private ResponseParser parser;
  private String host;
  
  /**
   * Constructs a new xml rpc wrapper.
   * 
   * @param moodleUrl - Moodle server URL
   * @param rpcServerUrl - the path to the RPC server
   * 
   * @throws ConnectorException - if the URL is not valid
   */
  public XmlRpcClientWrapper(String moodleUrl, String rpcServerUrl) throws ConnectorException {
    this.host = moodleUrl;
    try {
      xmlRpcClient = new XmlRpcClient(moodleUrl + rpcServerUrl, false);
    } catch (MalformedURLException muc) {
      throw new ConnectorException(
          "The given URL is not valid! URL: " + (moodleUrl + rpcServerUrl),
          ConnectorException.ILLEGAL_ARGUMENT, muc);
    }
    parser = new ResponseParser(this);
  }
  
  /**
   * The method returns the URL to the RPC lib.
   * 
   * @return - RPC lib URL
   */
  public String getRpcLibUrl() {
    return host + "/" + RPC_LIB_URL;
  }
  
  /**
   * The method wraps the call to receive the courses.
   * 
   * @param user - the user which course are interested
   * @param requestProps - the request properties
   * 
   * @return the user courses
   * 
   * @throws ConnectorException if an error is occurred
   */
  public IMoodleCourse[] getMoodleCources(IMoodleUser user, Map<String, String> requestProps) throws ConnectorException {
    Object[] arguments = new Long[] {user.getId()};
    try {
      xmlRpcClient.setRequestProperties(requestProps);
      Object result = xmlRpcClient.invoke(METHOD_URL_GET_USER_COURSES, arguments);
      if (!(result instanceof XmlRpcArray))
        return new IMoodleCourse[0];
      XmlRpcArray rpcResult = (XmlRpcArray) result; 
      IMoodleCourse[] courses = new IMoodleCourse[rpcResult.size()];
      MoodleCourseImpl currentCourse;
      for (int i = 0; i < courses.length; i++) {
        currentCourse = parser.parseMoodleCourse(rpcResult.getStruct(i));
        currentCourse.setRequestProperties(requestProps);
        currentCourse.setUser(user);
        courses[i] = currentCourse;
      }
      return courses;
    } catch (XmlRpcException e) {
      throw new ConnectorException(
          "Connection error! Executed method: " + METHOD_URL_GET_USER_COURSES,
          ConnectorException.CONNECTION_ERROR, e);
    } catch (XmlRpcFault e) {
      throw new ConnectorException(
          "Execution error! Executed method: " + METHOD_URL_GET_USER_COURSES,
          ConnectorException.FAULT_RESPONSE, e);
    }
  }
  
  /**
   * The method wraps the call to receive the user.
   * 
   * @param connection - the connection which contains account information
   * @param requestProps - the request properties
   * 
   * @return - the user
   * 
   * @throws ConnectorException if an error is occurred
   */
  public IMoodleUser getMoodleUser(IMoodleConnection connection, Map<String, String> requestProps) throws ConnectorException {
    String[] arguments = new String[] {connection.getUserName(), connection.getPassword()};
    try {
      xmlRpcClient.setRequestProperties(requestProps);
      Object resultObj = xmlRpcClient.invoke(METHOD_URL_GET_USER, arguments);
      if (!(resultObj instanceof XmlRpcStruct))
        throw new ConnectorException(
            "The moodle user is not returned correctly!", ConnectorException.FAULT_RESPONSE);
      XmlRpcStruct result = (XmlRpcStruct) resultObj;
      MoodleUserImpl user = parser.parseMoodleUser(result);
      Utility.copyCookies(xmlRpcClient.getResponseHeaderFields(), requestProps);
      user.setRequestProperties(requestProps);
      user.setConnection(connection);
      return user;
    } catch (XmlRpcException e) {
      throw new ConnectorException(
          "Connection error! Executed method: " + METHOD_URL_GET_USER,
          ConnectorException.CONNECTION_ERROR, e);
    } catch (XmlRpcFault e) {
      throw new ConnectorException(
          "Execution error! Executed method: " + METHOD_URL_GET_USER,
          ConnectorException.FAULT_RESPONSE, e);
    }
  }
  
  /**
   * The method wraps the call to receive the course module.
   * 
   * @param course - the course which modules will be received
   * @param modType - the type of the course modules
   * @param requestProps - the request properties
   * 
   * @return - the course modules
   * 
   * @throws ConnectorException if an error is occurred
   */
  public IMoodleModule[] getMoodleCourseModules(IMoodleCourse course, int modType, Map<String, String> requestProps) throws ConnectorException {
    String modName = (modType == IMoodleModule.RESOURCE) ?
        MODULE_NAME_RESOURCE : MODULE_NAME_ASSIGNMENT;
    String[] arguments = new String[] {course.getId().toString(), modName};
    try {
      xmlRpcClient.setRequestProperties(requestProps);
      Object result = xmlRpcClient.invoke(METHOD_URL_GET_COURSE_MODULES, arguments);
      if (!(result instanceof XmlRpcArray))
        return new IMoodleModule[0];
      XmlRpcArray rpcResponse = (XmlRpcArray) result;
      MoodleModuleImpl currentModule;
      IMoodleModule[] modules = new IMoodleModule[rpcResponse.size()];
      for (int i = 0; i < modules.length; i++) {
        currentModule = parser.parseMoodleCourseModule(rpcResponse.getStruct(i));
        currentModule.setCourse(course);
        currentModule.setRequestProperties(requestProps);
        currentModule.setType(modType);
        modules[i] = currentModule;
      }
      return modules;
    } catch (XmlRpcException e) {
      throw new ConnectorException(
          "Connection error! Executed method: " + METHOD_URL_GET_COURSE_MODULES,
          ConnectorException.CONNECTION_ERROR, e);
    } catch (XmlRpcFault e) {
      throw new ConnectorException(
          "Execution error! Executed method: " + METHOD_URL_GET_COURSE_MODULES,
          ConnectorException.FAULT_RESPONSE, e);
    }
  }
  
  /**
   * The method wraps the call to receive the module download URL.
   * 
   * @param course - the course which the module belongs
   * @param module - the module
   * @param requestProps - the request properties
   * 
   * @return - the download URL for the given module
   * 
   * @throws ConnectorException - if an error is occured
   */
  public String getMoodleModuleURL(IMoodleCourse course, IMoodleModule module, Map<String, String> requestProps) throws ConnectorException {
    String[] arguments = new String[] {course.getId().toString(), module.getId().toString()};
    try {
      xmlRpcClient.setRequestProperties(requestProps);
      Object result = xmlRpcClient.invoke(METHOD_URL_GET_RESOURCE_URL, arguments);
      if (!(result instanceof String))
        throw new ConnectorException(
            "The moodle module url is not returned correctly!", ConnectorException.FAULT_RESPONSE);
      return (String) result;
    } catch (XmlRpcException e) {
      throw new ConnectorException(
          "Connection error! Executed method: " + METHOD_URL_GET_RESOURCE_URL,
          ConnectorException.CONNECTION_ERROR, e);
    } catch (XmlRpcFault e) {
      throw new ConnectorException(
          "Execution error! Executed method: " + METHOD_URL_GET_RESOURCE_URL,
          ConnectorException.FAULT_RESPONSE, e);
    }
  }
  
}
